package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"github.com/jinzhu/gorm"
	client2 "kop/modules/achievement/client"
	"kop/modules/achievement/configuration/a_type"
	"time"

	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/modules/configuration/configuration/bufftype"
	"kop/modules/conquer/configuration/buff"
	"kop/modules/conquer/entity"
	"kop/modules/conquer/models"
	"kop/pb"
	"kop/pb/service"
	"kop/util/cd"
	"kop/util/date"
	"kop/util/rand"

	activityC "kop/modules/activity/client"
	captainC "kop/modules/captain/client"
	itemC "kop/modules/item/client"
	roleC "kop/modules/role/client"
)

type ConquerServiceHandler struct {
	context2.Context
}

// 已保存的数据 如果日期变更就重置为默认数据
func (h *ConquerServiceHandler) GetConquer(ctx context.Context, _ *empty.Empty, resp *pb.Conquer) error {

	var roleID = h.GetRoleID(ctx)

	var captains = captainC.GetCaptainsByReady(roleID)
	if len(captains) == 0 {
		return fmt.Errorf("len(captains) == 0")
	}

	var conquer, err = models.GetConquer(database.DB, roleID)
	if err != nil {
		if err != gorm.ErrRecordNotFound {

			return err
		} else {

			var session = database.DB.Begin()

			if conquer, err = models.CreateConquer(session, roleID); err != nil {
				session.Rollback()
				return err
			}

			if err = models.CreatePoints(session, roleID, h.GetServerID(ctx)); err != nil {
				session.Rollback()
				return err
			}

			session.Commit()
		}
	}

	if conquer.Date != date.Date(time.Now()) {

		conquer.Reset()
		models.SaveConquer(database.DB, &conquer)
	}

	*resp = h.buildPbConquer(conquer)
	return nil
}

//func (h *ConquerServiceHandler) GetShips(ctx context.Context, req *service.RoleRequest, resp *pb.ConquerShips) error {
//
//	var conquer, err = models.GetConquer(database.DB, req.RoleID)
//	if err != nil {
//		return err
//	}
//
//	if conquer.Type <= pb.ConquerType_ConquerDefault {
//		resp.Ships = conquer.Ships
//	} else {
//
//		var _buildings = buildingC.GetBuildings(conquer.RoleID)
//		var buildingMap = make(map[int32]*pb.Building, len(_buildings))
//		for _, v := range _buildings {
//			buildingMap[v.BuildingID] = v
//		}
//
//		resp.Ships = h.initShips(req.RoleID)
//		var m = make(map[int32]bool)
//		_ = json.Unmarshal(conquer.Ships2, &m)
//
//		for buildingID := range m {
//			for _, v := range resp.Ships {
//				if v.Building == buildingID {
//					v.State = pb.State_Finish
//				}
//			}
//		}
//	}
//
//	return nil
//}

// 随机一个对手
func (h *ConquerServiceHandler) RandOther(ctx context.Context, req *wrappers.BoolValue, resp *pb.RandOtherResp) error {

	var roleID = h.GetRoleID(ctx)
	var conquer, err = models.GetConquer(database.DB, roleID)
	if err != nil {
		return err
	}

	if conquer.Date != date.Date(time.Now()) {
		conquer.Reset()
	} else {
		conquer.Captain = nil
	}

	if conquer.HaveTarget() {
		return errors.ParamError
	}

	var prepare *itemC.Prepare

	if req.Value {

		const itemID = 101052
		if prepare, err = itemC.NewPrepare(roleID, h.GetServerID(ctx)).Prepare(itemID, 1); err != nil {
			return err
		}

		conquer.Extra += 1

	} else {
		if conquer.PlayNum < 1 {
			return errors.ParamError
		}
		conquer.PlayNum -= 1
	}

	var captains []*pb.Captain

	for _, v := range captainC.GetCaptainsByReady(conquer.RoleID) {

		var _, ok = conquer.CaptainIDs1[v.CaptainID]
		if v.Level < 60 || ok {
			continue
		}

		captains = append(captains, v)
	}

	if len(captains) == 0 {
		return errors.ParamError
	}

	var captain = captains[rand.Int(0, len(captains)-1)]

	conquer.CaptainIDs1[captain.CaptainID] = true
	conquer.Captain = &pb.ConquerCaptain{
		Captain: captain,
		COR:     captain.Attribute.COR,
		INT:     captain.Attribute.INT,
		Buffs:   []int32{},
	}

	var others []int32
	_ = json.Unmarshal(conquer.RoleIDs, &others)

	var role2, ok = models.RandOther(database.DB, roleID, h.GetServerID(ctx), others)
	if !ok {
		return nil
	}

	if req.Value {
		if err = prepare.AsyncRun(); err != nil {
			return err
		}
	}

	conquer.Type = pb.ConquerType_ConquerDefault
	conquer.RoleIDs, _ = json.Marshal(append(others, role2.RoleID))
	conquer.Role2 = &pb.ConquerRole{
		RoleID:    role2.RoleID,
		Points:    role2.Points,
		Name:      roleC.GetRole(role2.RoleID).Name,
		Captains2: captainC.GetCaptainsByReady(role2.RoleID),
	}

	models.SaveConquer(database.DB, &conquer)
	var res = h.buildPbConquer(conquer)
	resp.Conquer = &res
	resp.Ok = true

	// quest
	go client2.AddNumber(roleID, a_type.Conquer, 1)
	return nil
}

// 主动攻击 | 复仇
func (h *ConquerServiceHandler) Attack(ctx context.Context, req *service.ConquerAttackRequest, resp *pb.Conquer) error {

	var conquer, err = models.GetConquer(database.DB, req.RoleID)
	if err != nil {
		return err
	}

	if conquer.Date != date.Date(time.Now()) {
		conquer.Reset()
	} else {
		conquer.Captain = nil
	}

	if conquer.HaveTarget() {
		return errors.ParamError
	}

	if conquer.CaptainIDs2[req.Captain.CaptainID] {
		// 已使用过
		return errors.ParamError
	}

	point2, ok := models.GetPointsByRoleID(database.DB, req.RoleID2)
	if !ok {
		return errors.ParamError
	}

	conquer.CaptainIDs2[req.Captain.CaptainID] = true
	conquer.Captain = &pb.ConquerCaptain{
		Captain: req.Captain,
		COR:     req.Captain.Attribute.COR,
		INT:     req.Captain.Attribute.INT,
		Buffs:   []int32{},
	}

	if req.Type == pb.ConquerType_ConquerRevenge {

		models.SaveRevenge(req.RoleID, req.RoleID2)
	}

	conquer.Type = req.Type
	conquer.Role2 = &pb.ConquerRole{
		RoleID:    req.RoleID2,
		Points:    point2,
		Name:      roleC.GetRole(req.RoleID2).Name,
		Captains2: captainC.GetCaptainsByReady(req.RoleID2),
	}

	models.SaveConquer(database.DB, &conquer)
	*resp = h.buildPbConquer(conquer)
	return nil
}

// req.Captains 战后的船长数据
func (h *ConquerServiceHandler) Win(ctx context.Context, req *service.ConquerWinRequest, resp *pb.Conquer) error {

	var roleID = h.GetRoleID(ctx)
	var conquer, err = models.GetConquer(database.DB, roleID)
	if err != nil {
		return err
	}

	var conquerCopy = conquer

	var allDie = true
	for _, v := range conquer.Role2.Captains2 {
		if v.CaptainID == req.CaptainID2 {
			// 血量为0
			v.Attribute.COR = 0
		} else {
			if v.Attribute.COR > 0 {
				allDie = false
			}
		}
	}

	if allDie {

		// 全部战胜
		if conquer.Type == pb.ConquerType_ConquerDefault {
			conquer.CDStart()
		} else {
			conquer.RemoveTarget()
		}

	} else {
		// 更新我方船长数据
		conquer.NewBuff()
	}

	conquer.Captain.WinNum += 1
	conquer.Captain.Captain = req.Captain

	models.SaveConquer(database.DB, &conquer)

	*resp = h.buildPbConquer(conquer)

	_ = models.AddPoints(database.DB, roleID, 2)

	var lossPoints int32 = 1
	if conquer.Type == pb.ConquerType_ConquerSearch {
		lossPoints = 2
	}
	_ = models.SubPoints(database.DB, conquerCopy.Role2.RoleID, lossPoints)

	go func() {

		defer func() {
			_ = recover()
		}()

		if allDie {

			// 发战报
			var roles = roleC.GetRoles([]int32{roleID, conquerCopy.Role2.RoleID})

			_ = models.CreateReport(database.DB, entity.ConquerReport{
				RoleID:       conquerCopy.Role2.RoleID,
				ServerID:     roles[roleID].ServerID,
				Type:         conquerCopy.Type,
				WinNum:       conquerCopy.Captain.WinNum + 1,
				Attacker:     roleID,
				Photo:        roles[roleID].Photo,
				Level:        roles[roleID].Level,
				Power:        roles[roleID].Power,
				CaptainID:    conquerCopy.Captain.Captain.CaptainID,
				Points:       lossPoints * (conquerCopy.Captain.WinNum + 1),
				AttackerName: roles[roleID].Name,
				DefenderName: roles[conquerCopy.Role2.RoleID].Name,
			})
		}
	}()

	// 活动
	activityC.AddPoints(roleID, h.GetServerID(ctx), activityC.Conquer, 2)
	activityC.AddPoints(conquerCopy.Role2.RoleID, h.GetServerID(ctx), activityC.Conquer, int64(-lossPoints))

	return nil
}

func (h *ConquerServiceHandler) Loss(ctx context.Context, req *service.RoleRequest, resp *pb.Conquer) error {

	var conquer, err = models.GetConquer(database.DB, req.RoleID)
	if err != nil {
		return err
	}

	var conquerCopy = conquer

	if conquer.Type == pb.ConquerType_ConquerDefault {

		conquer.CDStart()
	} else {

		conquer.RemoveTarget()
	}

	models.SaveConquer(database.DB, &conquer)
	*resp = h.buildPbConquer(conquer)

	go func() {

		_ = recover()

		var lossPoints int32 = 1
		if conquerCopy.Type == pb.ConquerType_ConquerSearch {
			lossPoints = 2
		}

		// 发战报
		var roles = roleC.GetRoles([]int32{req.RoleID, conquerCopy.Role2.RoleID})

		_ = models.CreateReport(database.DB, entity.ConquerReport{
			RoleID:       conquerCopy.Role2.RoleID,
			ServerID:     roles[req.RoleID].ServerID,
			Type:         conquerCopy.Type,
			WinNum:       conquerCopy.Captain.WinNum + 1,
			Attacker:     req.RoleID,
			Photo:        roles[req.RoleID].Photo,
			Level:        roles[req.RoleID].Level,
			Power:        roles[req.RoleID].Power,
			CaptainID:    conquerCopy.Captain.Captain.CaptainID,
			Points:       lossPoints * (conquerCopy.Captain.WinNum + 1),
			AttackerName: roles[req.RoleID].Name,
			DefenderName: roles[conquerCopy.Role2.RoleID].Name,
		})

	}()

	return err
}

// 选择Buff
func (h *ConquerServiceHandler) Buff(ctx context.Context, req *service.DefaultRequest, resp *pb.Conquer) error {

	var buffConf, err = buff.GetByBuffID(req.Value)
	if err != nil {
		return err
	}

	conquer, err := models.GetConquer(database.DB, req.RoleID)
	if err != nil {
		return err
	}

	if !conquer.HaveNewBuff() {
		return errors.ParamError
	}

	var captain = conquer.Captain.Captain

	switch buffConf.BuffType {
	case bufftype.INT:

		captain.Attribute.INT += int32(float32(conquer.Captain.INT) * buffConf.Value)
	case bufftype.COR:

		//captain.Attribute.COR = int32(float64(captain.Attribute.COR) * float64(1+buffConf.Value))

	case bufftype.CriticalRate:

		captain.Skill2.CRTRate += buffConf.Value

	case bufftype.CriticalDamage:

		captain.Skill3.CRTDamage += buffConf.Value

	case bufftype.Healing: // 回血的

		captain.Attribute.COR += int32(float32(conquer.Captain.COR) * buffConf.Value)
	}

	conquer.SetBuff(buffConf.ID)
	models.SaveConquer(database.DB, &conquer)

	*resp = h.buildPbConquer(conquer)
	return nil
}

func (h *ConquerServiceHandler) ServerReports(ctx context.Context, req *wrappers.Int32Value, resp *pb.ConquerReports) error {

	var now = time.Now()
	var year, month, day = now.Date()
	var today = time.Date(year, month, day, 0, 0, 0, 0, time.Local)

	var n = 5

	var data []entity.ConquerReport
	if err := database.DB.Where("server_id = ?", req.Value).Where("win_num >= ?", n).Where("created_at >= ?", today).
		Limit(100).Order("id DESC").Find(&data).Error; err != nil {
		return err
	}

	resp.Reports = make([]*pb.ConquerReport, 0, len(data))
	for _, v := range data {

		resp.Reports = append(resp.Reports, &pb.ConquerReport{
			Attacker:     v.Attacker,
			Photo:        v.Photo,
			Level:        v.Level,
			Power:        v.Power,
			CaptainID:    v.CaptainID,
			WinNum:       v.WinNum,
			Points:       v.Points,
			AttackerName: v.AttackerName,
			DefenderName: v.DefenderName,
			Timestamp:    int32(v.CreatedAt.Unix()),
		})
	}

	return nil
}

func (h *ConquerServiceHandler) MyReports(ctx context.Context, req *service.RoleRequest, resp *pb.MyReportsResp) error {

	var res = models.GetReports(database.DB, req.RoleID, 100)
	resp.Reports = make([]*pb.ConquerReport, len(res))

	for k, v := range res {

		resp.Reports[k] = &pb.ConquerReport{
			Attacker:     v.Attacker,
			Photo:        v.Photo,
			Level:        v.Level,
			Power:        v.Power,
			CaptainID:    v.CaptainID,
			WinNum:       v.WinNum,
			Points:       v.Points,
			AttackerName: v.AttackerName,
			Type:         v.Type,
			Win:          v.Win,
			Timestamp:    int32(v.CreatedAt.Unix()),
		}
	}

	resp.IDs = models.GetRevenges(req.RoleID)
	return nil
}

func (h *ConquerServiceHandler) Search(ctx context.Context, req *wrappers.Int32Value, resp *pb.ConquerSearchResp) error {

	resp.Points, resp.Ok = models.GetPointsByRoleID(database.DB, req.Value)
	return nil
}

// 排行榜
func (h *ConquerServiceHandler) Ranking(_ context.Context, req *wrappers.Int32Value, resp *service.RankingPointsList) error {

	var data []entity.ConquerPoints

	if err := database.DB.Where("server_id = ?", req.Value).Order("points DESC").Order("updated_at ASC").Limit(200).Find(&data).Error; err != nil {
		return err
	}

	resp.Ranking = make([]*service.RankingPoints, len(data))
	for k, v := range data {
		resp.Ranking[k] = &service.RankingPoints{
			RoleID: v.RoleID,
			Points: int64(v.Points),
		}
	}

	return nil
}

func (h *ConquerServiceHandler) MyRanking(ctx context.Context, _ *empty.Empty, resp *pb.RankingNumber) (err error) {

	var data = entity.ConquerPoints{
		RoleID: h.GetRoleID(ctx),
	}
	if err = database.DB.First(&data).Error; err != nil {
		return err
	}

	var number int32
	if err = database.DB.Model(&data).Where("server_id = ? AND points > ?", h.GetServerID(ctx), data.Points).Count(&number).Error; err != nil {
		return err
	}

	//var number2 int32
	//if err = database.DB.Table("conquer_points").
	//	Where("server_id = ?", req.Value).Where("points = ?", data.Points).Where("updated_at < ?", data.UpdatedAt).
	//	Count(&number2).Error; err != nil {
	//	return err
	//}

	resp.Points = int64(data.Points)
	resp.Number = number + 1
	return nil
}

// 将船上船长数据复制到征服副本数据中
//func (h *ConquerServiceHandler) initShips(roleID int32) []*pb.ConquerShip {
//
//	var _captains = captainC.GetCaptains(roleID)
//	var captainMap = make(map[int32]*pb.Captain, len(_captains))
//	for _, v := range _captains {
//		captainMap[v.CaptainID] = v
//	}
//
//	var _buildings = buildingC.GetBuildings(roleID)
//	var buildingMap = make(map[int32]*pb.Building, len(_buildings))
//	for _, v := range _buildings {
//		buildingMap[v.BuildingID] = v
//	}
//
//	var ships = make([]*pb.ConquerShip, 0, 5)
//	for i := int32(201); i <= 205; i++ {
//
//		var m = map[int32]*pb.Captain{}
//		if building, ok := buildingMap[i]; ok {
//			m[1] = captainMap[building.Captain1]
//			m[2] = captainMap[building.Captain2]
//			m[3] = captainMap[building.Captain3]
//			m[4] = captainMap[building.Captain4]
//			m[5] = captainMap[building.Captain5]
//		}
//
//		ships = append(ships, &pb.ConquerShip{
//			Building: i,
//			Captains: m,
//			State:    pb.State_Default,
//		})
//	}
//
//	return ships
//}

func (h *ConquerServiceHandler) buildPbConquer(conquer entity.Conquer) pb.Conquer {

	var _, sec = cd.NewCD(time.Hour, 1).SetTime(conquer.CDTime).GetNum()

	return pb.Conquer{
		PlayNum: conquer.PlayNum,
		Extra:   conquer.Extra,
		CD:      sec,
		Points:  models.GetPoints(database.DB, conquer.RoleID),
		Captain: conquer.Captain,
		Role2:   conquer.Role2,
		IDs1:    conquer.CaptainIDs1,
		IDs2:    conquer.CaptainIDs2,
		Type:    conquer.Type,
	}
}

//func GetCaptain(conquer entity.Conquer) *pb.Captain {
//
//	var captainID = conquer.Captains[0].CaptainID
//
//	switch conquer.Type {
//	case pb.ConquerType_ConquerDefault:
//
//		for _, v := range conquer.Ships {
//			for _, captain := range v.Captains {
//				if captain != nil && captain.CaptainID == captainID {
//					return captain
//				}
//			}
//		}
//
//		return nil
//	default:
//
//		if captain, err := captainC.GetCaptain(conquer.RoleID, captainID); err != nil {
//			panic(err)
//		} else {
//			return captain
//		}
//	}
//}
